package com.api.apigateway.config;

import com.api.apiclientsdk.utils.SignUtils;
import com.api.apigateway.ratelimit.DynamicRedisRateLimiter;
import com.api.common.model.entity.User;
import com.api.common.service.InnerInterfaceInfoService;
import com.api.common.service.InnerUserService;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.dubbo.config.annotation.DubboReference;

import org.springframework.cloud.gateway.filter.ratelimit.KeyResolver;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.io.ClassPathResource;
import org.springframework.data.redis.connection.ReactiveRedisConnectionFactory;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.ReactiveRedisTemplate;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.data.redis.core.script.RedisScript;
import org.springframework.data.redis.serializer.*;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.scripting.support.ResourceScriptSource;
import reactor.core.publisher.Mono;

import java.util.List;
import java.util.Objects;
import java.util.Optional;

/**
 * @author: 崔老爷
 * Date: 2024/10/14 8:15
 * @Description:
 */

@Configuration
@Slf4j
public class RateLimiterConfig {
    @DubboReference
    private InnerUserService innerUserService;

    private final static String RATE_LIMIT="api:rate_limit:";


    @Bean
    @Primary
    public DynamicRedisRateLimiter dynamicRedisRateLimiter(
            ReactiveRedisTemplate<String, Object> reactiveRedisTemplate,
            RedisScript<List<Long>> redisScript) {
        return new DynamicRedisRateLimiter(reactiveRedisTemplate, redisScript);
    }
    /**
     * ✅ 主要的 RedisTemplate（用于存储复杂对象）
     * 使用 Jackson JSON 序列化
     */
    @Bean
    @Primary
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(connectionFactory);

        // Key 使用 String 序列化
        StringRedisSerializer stringSerializer = new StringRedisSerializer();

        // Value 使用 Jackson JSON 序列化（支持复杂对象）
        Jackson2JsonRedisSerializer<Object> jsonSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        objectMapper.activateDefaultTyping(
                LaissezFaireSubTypeValidator.instance,
                ObjectMapper.DefaultTyping.NON_FINAL
        );
        jsonSerializer.setObjectMapper(objectMapper);

        template.setKeySerializer(stringSerializer);
        template.setValueSerializer(jsonSerializer);
        template.setHashKeySerializer(stringSerializer);
        template.setHashValueSerializer(jsonSerializer);

        template.afterPropertiesSet();
        return template;
    }
    /**
     * ✅ 专门用于限流配置的 RedisTemplate
     * 使用字符串序列化（与 ReactiveRedisTemplate 保持一致）
     */
    @Bean("rateLimitRedisTemplate")
    public RedisTemplate<String, Object> rateLimitRedisTemplate(RedisConnectionFactory connectionFactory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(connectionFactory);

        // 全部使用字符串序列化
        StringRedisSerializer stringSerializer = new StringRedisSerializer();
        template.setKeySerializer(stringSerializer);
        template.setValueSerializer(stringSerializer);
        template.setHashKeySerializer(stringSerializer);
        template.setHashValueSerializer(stringSerializer);

        template.afterPropertiesSet();
        return template;
    }

    /**
     * ✅ ReactiveRedisTemplate（用于网关异步操作）
     */
    @Bean
    public ReactiveRedisTemplate<String, Object> reactiveRedisTemplate(
            ReactiveRedisConnectionFactory connectionFactory) {

        StringRedisSerializer stringSerializer = new StringRedisSerializer();
        // ✅ 创建一个适配 Object 类型的序列化器
        GenericToStringSerializer<Object> valueSerializer =
                new GenericToStringSerializer<>(Object.class);

        RedisSerializationContext<String, Object> context = RedisSerializationContext
                .<String, Object>newSerializationContext(stringSerializer)
                .key(stringSerializer)
                .value(valueSerializer)
                .hashKey(stringSerializer)
                .hashValue(stringSerializer)
                .build();

        return new ReactiveRedisTemplate<>(connectionFactory, context);
    }

    @Bean
    @SuppressWarnings("unchecked")
    public RedisScript<List<Long>> redisRequestRateLimiterScript() {
        DefaultRedisScript<List<Long>> redisScript = new DefaultRedisScript<>();
        redisScript.setScriptSource(new ResourceScriptSource(
                new ClassPathResource("META-INF/scripts/request_rate_limiter.lua")));
        redisScript.setResultType((Class<List<Long>>) (Class<?>) List.class);
        return redisScript;
    }

    /**
     * ✅ 简化的 KeyResolver
     * 只返回限流的标识符（accessKey），不做复杂的业务验证
     * 所有的业务逻辑验证（用户是否存在、签名校验等）都在 ApiGlobalFilter 中处理
     */
    @Bean
    public KeyResolver userAndIpKeyResolver() {
        return exchange -> {
            ServerHttpRequest request = exchange.getRequest();
            HttpHeaders headers = request.getHeaders();
            String accessKey = headers.getFirst("accessKey");
            
            // 如果没有 accessKey，使用 IP 地址作为限流标识
            if (StringUtils.isEmpty(accessKey)) {
                String ip = Optional.ofNullable(request.getRemoteAddress())
                        .map(addr -> addr.getAddress().getHostAddress())
                        .orElse("unknown");
                log.debug("请求没有 accessKey，使用 IP 作为限流标识: {}", ip);
                return Mono.just(RATE_LIMIT + "ip:" + ip);
            }
            
            log.debug("限流标识: {}", accessKey);
            return Mono.just(RATE_LIMIT + accessKey);
        };
    }

    public Mono<Void> handleNoAuth(ServerHttpResponse response) {
        response.setStatusCode(HttpStatus.FORBIDDEN);
        return response.setComplete();
    }

    /**
     * X-User-Id 是前端传递的用户标识符（比如用户的 ID）。你可以根据实际的请求头字段来调整代码。
     * 如果用户没有传递 X-User-Id，则默认为 "anonymous"，即对匿名用户进行限流。s
     *
     * @return
     */
//    @Bean
    public KeyResolver userKeyResolver() {
        return exchange -> {
            // 根据请求头中的 "X-User-Id" 来识别用户
            String userId = exchange.getRequest().getHeaders().getFirst("X-User-Id");
            return Mono.just(Optional.ofNullable(userId).orElse("anonymous"));
        };
    }

    /**
     * 基于 IP 地址的限流
     * 基于用户身份认证后的 Token。
     * 基于请求的 IP 地址（适用于匿名用户）。
     * 你可以根据实际业务需求定义更多维度的 KeyResolver。
     *
     * @return
     */
//    @Bean
    public KeyResolver ipKeyResolver() {
        return exchange -> Mono.just(exchange.getRequest().getRemoteAddress().getHostName());
    }
}
